package pl.ydp.components.mathcell.mathrecognizer.geometry.extractor.features
{
	import flash.geom.Point;
	
	import pl.ydp.components.mathcell.mathrecognizer.engine.Parameters;
	import pl.ydp.components.mathcell.mathrecognizer.geometry.EuclideanVector;
	
	public class FeaturesCross
	{
		//------------------------------------------------------------------

		public static function findLinesCrossing(refBegin:Point, refEnd:Point, testBegin:Point, testEnd:Point):Boolean{
							
				var isCrossing:Boolean;
				
				var refMin:Point = new Point;
				var refMax:Point = new Point;
				var testMin:Point = new Point;
				var testMax:Point = new Point;
				
				if (refBegin.x < refEnd.x){
					refMin.x = refBegin.x;
					refMax.x = refEnd.x;
				} else {
					refMin.x = refEnd.x;
					refMax.x = refBegin.x;
				}
				
				if (refBegin.y < refEnd.y){
					refMin.y = refBegin.y;
					refMax.y = refEnd.y;
				} else {
					refMin.y = refEnd.y;
					refMax.y = refBegin.y;
				}
				
				if (testBegin.x < testEnd.x){
					testMin.x = testBegin.x;
					testMax.x = testEnd.x;
				} else {
					testMin.x = testEnd.x;
					testMax.x = testBegin.x;
				}
				
				if (testBegin.y < testEnd.y){
					testMin.y = testBegin.y;
					testMax.y = testEnd.y;
				} else {
					testMin.y = testEnd.y;
					testMax.y = testBegin.y;
				}
				
				
				var refA:Number = (refBegin.y - refEnd.y) / (refBegin.x - refEnd.x);
				var refB:Number = refBegin.y - refA * refBegin.x;
				
				var testA:Number = (testBegin.y - testEnd.y) / (testBegin.x - testEnd.x);
				var testB:Number = testBegin.y - testA * testBegin.x;
				
				var X:Number = -1 * (testB - refB) / (testA - refA);
				var Y:Number = refA * X + refB;
				
				if (((X >= refMin.x  &&  X <= refMax.x) ||  Math.abs(refA) > 16)  &&
					((Y >= refMin.y  &&  Y <= refMax.y) ||  Math.abs(refB) < 1/16)   && 
					((X >= testMin.x  &&  X <= testMax.x) ||  Math.abs(testA) > 16)   &&
					((Y >= testMin.y  &&  Y <= testMax.y) ||  Math.abs(testB) > 1/16) ){
					isCrossing = true;	
				} else {
					isCrossing = false
				}
				
				return isCrossing;
		}
		
		//------------------------------------------------------------------

		public static function findSelfCrossings(vectors:Array, lengthCoeff:Number = -1, includeClosureClosing:Boolean = false):Array{
			
			if (vectors.length == 0){
				return new Array;
			}
			
			var v:Number, test:Number;
			var MONSTER_VECTOR_LENGTH:Number;
			if (lengthCoeff == -1){
				MONSTER_VECTOR_LENGTH = Math.round(Parameters.MIN_LINE_LENGTH);
			} else {
				MONSTER_VECTOR_LENGTH = lengthCoeff;
			}
			var refVector:EuclideanVector;
			var testVector:EuclideanVector;
			var refBegin:Point;
			var refEnd:Point;
			var testBegin:Point
			var testEnd:Point;
			
			var crossFound:Boolean = false;
			var crossings:Array = new Array;
						
			var As:Array = new Array;
			var Bs:Array = new Array;
			var keys:Array = new Array;
			var crossPoints:Array = new Array;
			var vIndexes:Array = new Array;
			var testIndexes:Array = new Array;
			var x:Number, y:Number;
			
			var vMin:Point;
			var testMin:Point;
			var vMax:Point;
			var testMax:Point;
			
			// find key elements in vectors array
			
			keys.push(0);
			
			for (v = MONSTER_VECTOR_LENGTH ; v < vectors.length - MONSTER_VECTOR_LENGTH/2; v += MONSTER_VECTOR_LENGTH ){
				keys.push(v);
			}
			
			keys.push(vectors.length-1);
			if (includeClosureClosing){
				keys.push(0);
			}
			
			// find A & B
			
			for (v = 0 ; v < keys.length-1 ; v ++ ){
				As.push(  (vectors[keys[v]].point.y - vectors[keys[v+1]].point.y) / (vectors[keys[v]].point.x - vectors[keys[v+1]].point.x)  );
				Bs.push(vectors[keys[v]].point.y - As[As.length-1] * vectors[keys[v]].point.x);
			}
			
			for (v = 0 ; v < As.length ; v ++ ){
				
				vMin = new Point;
				vMax = new Point;
				
				if (vectors[keys[v]].point.x < vectors[keys[v+1]].point.x){
					vMin.x = vectors[keys[v]].point.x;
					vMax.x = vectors[keys[v+1]].point.x;
				} else {
					vMin.x = vectors[keys[v+1]].point.x;
					vMax.x = vectors[keys[v]].point.x;
				}
				
				if (vectors[keys[v]].point.y < vectors[keys[v+1]].point.y){
					vMin.y = vectors[keys[v]].point.y;
					vMax.y = vectors[keys[v+1]].point.y;
				} else {
					vMin.y = vectors[keys[v+1]].point.y;
					vMax.y = vectors[keys[v]].point.y;
				}
				
				for (test = v+1 ; test < As.length ; test ++ ){
					
					x = -1 * (Bs[v] - Bs[test]) / (As[v] - As[test]);
					y = As[v] * x + Bs[v];
					
					testMin = new Point;
					testMax = new Point;
					
					if (vectors[keys[test]].point.x < vectors[keys[test+1]].point.x){
						testMin.x = vectors[keys[test]].point.x;
						testMax.x = vectors[keys[test+1]].point.x;
					} else {
						testMin.x = vectors[keys[test+1]].point.x;
						testMax.x = vectors[keys[test]].point.x;
					}
					
					if (vectors[keys[test]].point.y < vectors[keys[test+1]].point.y){
						testMin.y = vectors[keys[test]].point.y;
						testMax.y = vectors[keys[test+1]].point.y;
					} else {
						testMin.y = vectors[keys[test+1]].point.y;
						testMax.y = vectors[keys[test]].point.y;
					}
					
					if (((x > vMin.x  &&  x < vMax.x)  ||  Math.abs(As[v]) > 16)  &&
						((y > vMin.y  &&  y < vMax.y)  ||  Math.abs(Bs[v]) < 1/16)  &&
						((x > testMin.x  &&  x < testMax.x)  ||  Math.abs(As[test]) > 16)  &&
						((y > testMin.y  &&  y < testMax.y)  ||  Math.abs(Bs[test]) < 1/16)){
						crossPoints.push(new Point(x,y));
						vIndexes.push(v);
						testIndexes.push(test);
					}  
					
				} // end for (test...
				
			} // end for (v...
			
			
			for (v = 0 ; v < crossPoints.length ; v ++ ){
				
				crossings.push(new Array(
					findClosestVectorIndex(vectors, keys[vIndexes[v]], keys[vIndexes[v]+1], crossPoints[v]),
					findClosestVectorIndex(vectors, keys[testIndexes[v]], keys[testIndexes[v]+1], crossPoints[v])
					)
				);
				
			}// end for (v...
			
			return crossings;
			
		}
		
		//------------------------------------------------------------------
		
		private static function findClosestVectorIndex(vectors:Array, fromIndex:Number, toIndex:Number, point:Point):Number{

			var test:Number;
		
			var minDistance:Number;
			var minDistanceIndex:Number;
			var currDistance:Number;
						
			minDistance = Point.distance(vectors[fromIndex].point, point );
			minDistanceIndex = fromIndex;
			
			for (test = fromIndex+1 ; test < toIndex ; test ++ ){
				
				currDistance = Point.distance(vectors[test].point, point)
				
				if (currDistance <  minDistance){
					minDistance = currDistance;
					minDistanceIndex = test;
				}
				
			} // end for (test...	
			
			return minDistanceIndex;		
		}

	}
}